home *** CD-ROM | disk | FTP | other *** search
/ Gold Medal Software 2 / Gold Medal Software Volume 2 (Gold Medal) (1994).iso / music / ptmid.arj / PTMIDINP.C < prev    next >
C/C++ Source or Header  |  1994-01-08  |  12KB  |  397 lines

  1. /*
  2.  * ptmidinp.c: MIDI input module for for ptmid. Reads a MIDI file and
  3.  * creates a structure representing it.
  4.  *
  5.  * Author: Andrew Scott  (c)opyright 1994
  6.  *
  7.  * Date: 17/11/1993 ver 0.0
  8.  *       8/1/1994   ver 0.1
  9.  */
  10.  
  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include "ptmid.h"
  14.  
  15. #define ODD(x) (x & 1)
  16. #define MYROUTINE
  17.  
  18. typedef unsigned long VLQ; /** VLQ is a variable length quantity **/
  19.  
  20. int rgbPatch[16], wDivision;
  21. unsigned wQuant;
  22. NRL *rgpnrl[128];
  23. Tune *ptuneMain, *ptuneCurr;
  24.  
  25. /*
  26.  * Init: Yes.. you guessed it. Initializes variables and stuff.
  27.  */
  28. static void Init()
  29. {
  30.     int i;
  31.  
  32.     for (i = 16; i--; ) /** Clear current instrument array **/
  33.         rgbPatch[i] = 0;
  34.     for (i = 128; i--; ) /** Clear hanging-note array **/
  35.         rgpnrl[i] = NULL;
  36. }
  37.  
  38. /*
  39.  * ValidquantSz: Takes a lower-case string and checks to see if it is a
  40.  * legal quantize fraction. If not, zero is returned, else value of
  41.  * string is returned (+1 if a triplet case).
  42.  */
  43. int ValidquantSz(Sz szQuant)
  44. {
  45.     int bFrac;
  46.     Sz szEnd;
  47.  
  48.     if ((bFrac = strtol(szQuant, &szEnd, 10))) /** If a number **/
  49.         if ('t' == *szEnd) /** possibly followed by a 't', then valid **/
  50.             bFrac++;
  51.     return bFrac;
  52. }
  53.  
  54. /*
  55.  * VlqFromPfile: Reads bytes from given file until a variable length
  56.  * quantity is decoded. Returns that vlq.
  57.  */
  58. VLQ VlqFromPfile(FILE *pfile)
  59. {
  60.     int b;
  61.     VLQ vlqRead;
  62.  
  63. #ifdef MYROUTINE
  64.  
  65.     vlqRead = 0;
  66.     while ((b = getc(pfile)) & 128 && EOF != b)
  67.         vlqRead = (vlqRead << 7) + (b & 127);
  68.     return (vlqRead << 7) + b;
  69.  
  70. #else
  71.  
  72.     if ((vlqRead = getc(pfile)) & 128) {
  73.         vlqRead &= 127;
  74.         do
  75.             vlqRead = (vlqRead << 7) + ((b = getc(pfile)) & 127);
  76.         while (b & 128 && EOF != b);
  77.     }
  78.     return vlqRead;
  79.  
  80. #endif
  81. }
  82.  
  83. /*
  84.  * Addnote: Given pitch, instrument, and volume will add that note to the
  85.  * array of playing notes.
  86.  */
  87. void Addnote(int pitch, int inst, int vol)
  88. {
  89.     NRL *pnrlT;
  90.  
  91.     if (0 > pitch || 127 < pitch)
  92.         return;
  93.     pnrlT = (NRL *) malloc(sizeof(NRL)); /** Allocate space **/
  94.     pnrlT->pnrl = rgpnrl[pitch];
  95.     rgpnrl[pitch] = pnrlT; /** Attach to front of list in hanging-note array **/
  96.     pnrlT->inst = inst;
  97.     pnrlT->vol = vol;
  98.     pnrlT->ptuneNow = ptuneCurr;
  99. }
  100.  
  101. /*
  102.  * PeiRequestPtune: Returns a pointer to a new event structure at the
  103.  * position in the tune specified.
  104.  */
  105. EI *PeiRequestPtune(Tune *ptune)
  106. {
  107.     EI *pei;
  108.  
  109.     pei = (EI *) malloc(sizeof(EI)); /** Allocate space for event **/
  110.     pei->peiNext = ptune->pei;
  111.     ptune->pei = pei; /** Attach to front of event list at tune position **/
  112.     return pei;
  113. }
  114.  
  115. /*
  116.  * PeiLocatePtune: Given a duration and a position in the tune, will search
  117.  * through for chord events at that position with matching duration.
  118.  */
  119. EI *PeiLocatePtune(Tune *ptune, unsigned long durat)
  120. {
  121.     EI *pei;
  122.  
  123.     for (pei = ptune->pei; NULL != pei && !(pei->cni && pei->effect == durat); )
  124.         pei = pei->peiNext;
  125.     return pei;
  126. }
  127.  
  128. /*
  129.  * Endnote: Given a pitch and instrument, will remove a note from array
  130.  * of playing notes and put it in a chord in the tune.
  131.  */
  132. void Endnote(int pitch, int inst)
  133. {
  134.     NRL *pnrlT, *pnrlOld;
  135.     unsigned long durat;
  136.     EI *peiT;
  137.     NI *pniT;
  138.  
  139.     if (0 > pitch || 127 < pitch)
  140.         return;
  141.     for (pnrlT = rgpnrl[pitch]; NULL != pnrlT && pnrlT->inst != inst; ) {
  142.         pnrlOld = pnrlT;
  143.         pnrlT = pnrlT->pnrl;
  144.     } /** Find instrument in hanging-note array **/
  145.     if (NULL == pnrlT)
  146.         return;
  147.  
  148.     if ((durat = ptuneCurr->count - pnrlT->ptuneNow->count) == 0)
  149.         durat = wQuant; /** Calculate its (quantized) duration **/
  150.     if ((peiT = PeiLocatePtune(pnrlT->ptuneNow, durat)) == NULL) {
  151.         peiT = PeiRequestPtune(pnrlT->ptuneNow); /** If must start a new chord **/
  152.         peiT->effect = durat;
  153.         peiT->cni = 1;
  154.         pniT = (NI *) malloc(sizeof(NI)); /** start new note list **/
  155.         peiT->pni = pniT;
  156.     } else { /** Else **/
  157.         peiT->pni = (NI *) realloc(peiT->pni, sizeof(NI) * ++(peiT->cni));
  158.         pniT = peiT->pni + peiT->cni - 1; /** add to old note list **/
  159.     }
  160.     pniT->inst = inst; /** Update note info **/
  161.     pniT->pitch = pitch;
  162.     pniT->vol = pnrlT->vol;
  163.  
  164.     if (rgpnrl[pitch] == pnrlT) /** Remove note from hanging-note array **/
  165.         rgpnrl[pitch] = pnrlT->pnrl;
  166.     else
  167.         pnrlOld->pnrl = pnrlT->pnrl;
  168.     free(pnrlT);
  169. }
  170.  
  171. /*
  172.  * VlqInterpPpfile: Reads a file pointer and gathers all notes at this
  173.  * instant, storing them on the note stack. Returns ticks until next
  174.  * collection of notes. A running status of current channel is maintained.
  175.  */
  176. VLQ VlqInterpPpfile(FILE **ppfile, unsigned *pbStat)
  177. {
  178.     unsigned bEvent;
  179.     VLQ vlqT = 0;
  180.  
  181.     while (0 == vlqT) { /** While doing simultaneous events.. **/
  182.         bEvent = (unsigned) getc(*ppfile); /** Get first data byte **/
  183.         if (0x80 <= bEvent && 0xEF >= bEvent) { /** If a command **/
  184.             *pbStat = bEvent; /** update running-status byte **/
  185.             bEvent = (unsigned) getc(*ppfile); /** and get next data byte **/
  186.         }
  187.         if (0xF0 == bEvent || 0xF7 == bEvent) /** If a sys-exclusive message **/
  188.             fseek(*ppfile, VlqFromPfile(*ppfile), SEEK_CUR); /** skip it **/
  189.         else if (0xFF == bEvent) { /** Else if a meta event **/
  190.             bEvent = (unsigned) getc(*ppfile); /** get type of event **/
  191.             vlqT = VlqFromPfile(*ppfile); /** and length **/
  192.             if (0x2F == bEvent) { /*** If termination event ***/
  193.                 fclose(*ppfile); /*** close handle ***/
  194.                 *ppfile = NULL;
  195.                 vlqT = 0;
  196.                 break; /*** and terminate ***/
  197.             }
  198.             if (0x51 == bEvent) { /*** Else if tempo event ***/
  199.                 unsigned long t;
  200.                 EI *pei;
  201.  
  202.                 t = (unsigned long) getc(*ppfile) << 16; /*** get value ***/
  203.                 t += (unsigned long) getc(*ppfile) << 8;
  204.                 t += (unsigned long) getc(*ppfile);
  205.                 pei = PeiRequestPtune(ptuneCurr);
  206.                 pei->effect = 60000000L / wQuant * wDivision / t; /*** and convert ***/
  207.                 pei->cni = 0;
  208.             } else
  209.                 fseek(*ppfile, vlqT, SEEK_CUR); /*** Else skip event ***/
  210.         } else
  211.             switch (*pbStat & 0xF0) { /** Else must be a midi event.. **/
  212.                 case 0x80:
  213.                 case 0x90: { /** Note on/off **/
  214.                     unsigned bVol, bChan;
  215.  
  216.                     bVol = getc(*ppfile);
  217.                     bChan = *pbStat & 0x0F;
  218.                     if (0 < bVol && 0x90 <= *pbStat)
  219.                         if (bChan != bDrumch)
  220.                             Addnote(bEvent, rgbPatch[bChan], bVol);
  221.                         else
  222.                             Addnote(0, -1 - bEvent, bVol);
  223.                     else
  224.                         if (bChan != bDrumch)
  225.                             Endnote(bEvent, rgbPatch[bChan]);
  226.                         else
  227.                             Endnote(0, -1 - bEvent);
  228.                     break;
  229.                 }
  230.                 case 0xA0: /** Polyphonic Key Pressure **/
  231.                 case 0xB0: /** Controller change **/
  232.                 case 0xE0: /** Pitch Wheel change **/
  233.                     getc(*ppfile);
  234.                     break;
  235.                 case 0xC0: /** Program change **/
  236.                     rgbPatch[*pbStat - 0xC0] = bEvent;
  237.                     break;
  238.                 case 0xD0: /** Channel Pressure **/
  239.                     break;
  240.             }
  241.         vlqT = VlqFromPfile(*ppfile);
  242.     }
  243.     return vlqT;
  244. }
  245.  
  246. /*
  247.  * Freearray: Performs a free on all structures left in rgpnrl array.
  248.  * Assume these notes are rebels.
  249.  */
  250. void Freearray()
  251. {
  252.     int i = 128;
  253.     NRL *pnrlT, *pnrlT2;
  254.  
  255.     while (i--) /** Go through hanging-note array **/
  256.         for (pnrlT = rgpnrl[i]; NULL != pnrlT; ) { /** freeing each list **/
  257.             pnrlT2 = pnrlT->pnrl;
  258.             free(pnrlT);
  259.             pnrlT = pnrlT2;
  260.         }
  261. }
  262.  
  263. /*
  264.  * PtuneLoadPfile: Given the filename of a MIDI file, parse it and return
  265.  * a pointer to a collection of chords (a Tune structure). If MIDI file
  266.  * cannot be processed, NULL is returned.
  267.  */
  268. Tune *PtuneLoadFn(Sz FnMIDI)
  269. {
  270.     FILE *pfile, **ppfile;
  271.     unsigned long cb, *pvlqWait, vlqMin = -1, vlqT, wCount, wNcount;
  272.     unsigned ippfile, ippfileMax, *pbStatus, wQuant2;
  273.     int cppfile;
  274.  
  275.     Init();
  276.     pfile = fopen(FnMIDI, "rb");
  277.     if ('M' != getc(pfile) || 'T' != getc(pfile) || 'h' != getc(pfile) ||
  278.      'd' != getc(pfile) || 0 != getc(pfile) || 0 != getc(pfile) ||
  279.      0 != getc(pfile) || 6 != getc(pfile) || 0 != getc(pfile) ||
  280.      1 < (unsigned) getc(pfile)) {
  281.         fclose(pfile);
  282.         return NULL; /** Only process type 0 or type 1 general MIDI files **/
  283.     }
  284.  
  285.     ippfileMax = (unsigned) getc(pfile) << 8;
  286.     ippfileMax += (unsigned) getc(pfile); /** Get # tracks **/
  287.     wDivision = (unsigned) getc(pfile) << 8;
  288.     wDivision += (unsigned) getc(pfile); /** Get ticks for a beat **/
  289.  
  290.     if (fseek(pfile, 23, SEEK_SET)) {
  291.         fclose(pfile);
  292.         return NULL; /** Error if MIDI file is smaller than 23 bytes **/
  293.     }
  294.     if (fNocopy && getc(pfile) == 0xFF && getc(pfile) == 0x02) {
  295.         fclose(pfile);
  296.         if (!fQuiet)
  297.             printf("** NOCOPY option set and copyright notice found in file **\n");
  298.         return NULL; /** Error if Nocopy and copyright notice exists **/
  299.     }
  300.     if (32767 < wDivision) {
  301.         if (!fQuiet)
  302.             printf("** Slack programmer error -- SMPTE frames not supported **\n");
  303.         fclose(pfile);
  304.         return NULL;
  305.     }
  306.  
  307.     if (ODD(wQuantval)) /** Calculate quantize ticks from quantize fraction **/
  308.         wQuant = wDivision * 8 / (3 * (wQuantval - 1));
  309.     else
  310.         wQuant = wDivision * 4 / wQuantval;
  311.     wQuant2 = wQuant/2;
  312.     if (!fQuiet) {
  313.         printf("Ticks to quantize: %u\n", wQuant);
  314.         printf("Number of tracks: %d\n", ippfileMax);
  315.     }
  316.  
  317.     ppfile = (FILE **) malloc(sizeof(FILE *) * ippfileMax);
  318.     pvlqWait = (VLQ *) malloc(sizeof(VLQ) * ippfileMax);
  319.     pbStatus = (unsigned *) malloc(sizeof(unsigned) * ippfileMax);
  320.  
  321.     fseek(pfile, 18, SEEK_SET);
  322.     /** Put file pointers at the start of each track in file **/
  323.     for (ippfile = 0; !feof(pfile) && ippfile < ippfileMax; ippfile++) {
  324.         cb = (unsigned long) getc(pfile) << 24;
  325.         cb += (unsigned long) getc(pfile) << 16;
  326.         cb += (unsigned long) getc(pfile) << 8;
  327.         cb += (unsigned long) getc(pfile);
  328.         if ((ppfile[ippfile] = fopen(FnMIDI, "rb")) == NULL) {
  329.             fprintf(stderr, "ptmid: No more files can be opened\n");
  330.             exit(1);
  331.         }
  332.         fseek(ppfile[ippfile], ftell(pfile), SEEK_SET);
  333.         if ((pvlqWait[ippfile] = VlqFromPfile(ppfile[ippfile])) < vlqMin)
  334.             vlqMin = pvlqWait[ippfile]; /** Find minimum time to first event **/
  335.         pbStatus[ippfile] = 0x90;
  336.         fseek(pfile, cb + 4, SEEK_CUR);
  337.     }
  338.     fclose(pfile);
  339.     if (ippfile != ippfileMax) {
  340.         printf("** MIDI file ends prematurely **\n");
  341.         free(ppfile);
  342.         free(pvlqWait);
  343.         free(pbStatus);
  344.         return NULL;
  345.     }
  346.  
  347.     ptuneMain = ptuneCurr = (Tune *) malloc(sizeof(Tune));
  348.     wCount = vlqMin; /** Start from first event **/
  349.     wNcount = (wCount + wQuant2) % wQuant;
  350.     ptuneMain->count = wCount + wQuant2 - wNcount;
  351.     wNcount = wQuant - wNcount;
  352.     ptuneMain->ptune = NULL;
  353.     ptuneMain->pei = NULL;
  354.     cppfile = 1;
  355.     while (0 < cppfile) { /** While still tracks in file to process **/
  356.         cppfile = 0;
  357.         vlqT = -1;
  358.         for (ippfile = 0; ippfile < ippfileMax; ippfile++) /** With each track.. **/
  359.             if (NULL != ppfile[ippfile]) { /** If not finished **/
  360.                 cppfile++;
  361.                 /**
  362.                  ** Must keep all tracks in sync, if events occurring on this track
  363.                  ** at this instant, then interpret them. Also note when next event
  364.                  ** will occur (ie. minimum ticks to next event)
  365.                  **/
  366.                 if ((pvlqWait[ippfile] -= vlqMin) == 0)
  367.                     pvlqWait[ippfile] = VlqInterpPpfile(&ppfile[ippfile], pbStatus +
  368.                         ippfile);
  369.                 if (pvlqWait[ippfile] < vlqT)
  370.                     vlqT = pvlqWait[ippfile];
  371.             }
  372.  
  373.         vlqMin = vlqT;
  374.         wCount += vlqMin;
  375.         if (wNcount <= vlqMin) { /** If need to advance to new quanta **/
  376.             if ((ptuneCurr->ptune = (Tune *) malloc(sizeof(Tune))) == NULL) {
  377.                 fprintf(stderr, "ptmid: Cannot allocate any more memory\n");
  378.                 exit(1);
  379.             } /** allocate **/
  380.             ptuneCurr = ptuneCurr->ptune; /** and initialize **/
  381.             wNcount = (wCount + wQuant2) % wQuant;
  382.             ptuneCurr->count = wCount + wQuant2 - wNcount;
  383.             wNcount = wQuant - wNcount;
  384.             ptuneCurr->ptune = NULL;
  385.             ptuneCurr->pei = NULL;
  386.         } else
  387.             wNcount -= vlqMin; /** Else decrememnt "new quanta" count **/
  388.     }
  389.  
  390.     Freearray();
  391.     free(pvlqWait);
  392.     free(pbStatus);
  393.     free(ppfile);
  394.  
  395.     return ptuneMain;
  396. }
  397.